package com.ctshk.rpc.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ctshk.common.dto.Result;
import com.ctshk.common.dto.payment.CallPayDTO;
import com.ctshk.common.enums.IsDeletedCode;
import com.ctshk.common.enums.SystemBusinessType;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.exception.BusinessException;
import com.ctshk.common.utils.Assert;
import com.ctshk.common.utils.EntityUtil;
import com.ctshk.common.utils.ValidatorUtil;
import com.ctshk.rpc.order.air.service.IAirOrderService;
import com.ctshk.rpc.order.airexpress.service.IAirportExpressOrderAppService;
import com.ctshk.rpc.order.goods.service.IOrderService;
import com.ctshk.rpc.order.hotel.service.IHotelOrderH5Service;
import com.ctshk.rpc.order.localfun.service.ILocalFunAppOrderService;
import com.ctshk.rpc.order.scenic.service.IScenicOrderService;
import com.ctshk.rpc.order.scenic.service.app.IAirshowAppOrderService;
import com.ctshk.rpc.order.season.service.ISeasonOrderService;
import com.ctshk.rpc.order.tour.service.ISeriesOrderService;
import com.ctshk.rpc.order.train.service.ITrainOrderAppService;
import com.ctshk.rpc.payment.dto.req.PayRefundReq;
import com.ctshk.rpc.payment.entity.PayRecord;
import com.ctshk.rpc.payment.entity.RefundRecord;
import com.ctshk.rpc.payment.enums.PayOrderStatus;
import com.ctshk.rpc.payment.enums.PaymentMethod;
import com.ctshk.rpc.payment.enums.PaymentMethodClass;
import com.ctshk.rpc.payment.enums.RefundStatus;
import com.ctshk.rpc.payment.mapper.PayRecordMapper;
import com.ctshk.rpc.payment.mapper.RefundRecordMapper;
import com.ctshk.rpc.payment.req.CallPayReq;
import com.ctshk.rpc.payment.req.PayCommonReq;
import com.ctshk.rpc.payment.req.RefundReq;
import com.ctshk.rpc.payment.service.IPayRecordService;
import com.ctshk.rpc.payment.service.IPayService;
import com.ctshk.rpc.payment.strategy.PayContext;
import com.ctshk.rpc.payment.utils.PayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Map;

/**
 * @BelongsProject: ctshk-cloud
 * @BelongsPackage: com.ctshk.rpc.payment.service.impl
 * @Author: hef
 * @CreateTime: 2021-04-20 18:03
 * @Description: 描述
 */
@Slf4j
@DubboService
public class IPayServiceImpl implements IPayService {
    @Autowired
    private IPayRecordService iPayRecordService;
    @Autowired
    private PayRecordMapper recordMapper;
    @Autowired
    private RefundRecordMapper refundRecordMapper;
    @DubboReference
    private IAirportExpressOrderAppService airportExpressOrderAppService;
    @DubboReference
    private IHotelOrderH5Service hotelOrderH5Service;
    @DubboReference
    private IOrderService orderService; // 商品
    @DubboReference
    private IAirOrderService airOrderService;
    @DubboReference
    private ILocalFunAppOrderService iLocalFunAppOrderService;
    @DubboReference
    private IScenicOrderService scenicOrderService;
    @DubboReference
    private ITrainOrderAppService trainOrderAppService;
    @DubboReference
    private ISeriesOrderService seriesOrderService;
    @DubboReference
    private IAirshowAppOrderService iAirshowAppOrderService;
    @DubboReference
    private ISeasonOrderService iSeasonOrderService;

    @Value("${spring.profiles.active}")
    private String active;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result pay(PayCommonReq req, Long userId) {
        return Result.success(null);
    }

    @Override
    public Result callPay(CallPayReq callPayReq) {
        log.info("【支付服务-查询业务订单数据】调用入参, data:{}", callPayReq);
        Assert.nonNull(callPayReq.getId(), SystemError.PAY_22022);
        LocalDateTime now = LocalDateTime.now();
        PayCommonReq req = new PayCommonReq();
        req.setBusinessTypeId(callPayReq.getBusinessTypeId());
        req.setOrderId(callPayReq.getId());
        req.setPaymentMethod(PaymentMethod.getByCode(callPayReq.getPaymentMethodCode()));
        //去数据库获取订单信息
        PayRecord payRecord = recordMapper.selectByOrderId(callPayReq.getId());
        if (null == payRecord) {
            // 查询业务订单数据
            SystemBusinessType systemBusinessType = SystemBusinessType.parseCode(callPayReq.getBusinessTypeId());
            CallPayDTO callPayDTO = null;
            switch (systemBusinessType) {
                case HOTEL:
                    callPayDTO = hotelOrderH5Service.getCallPayInfo(callPayReq.getId());
                    break;
                case AIR_EXPRESS:
                    callPayDTO = airportExpressOrderAppService.getCallPayInfo(callPayReq.getId());
                    break;
                case COMMODITY:
                    callPayDTO = orderService.getCallPayInfo(callPayReq.getId());
                    break;
                case AIR_TICKET:
                    callPayDTO = airOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                case TRAIN_TICKET:
                    callPayDTO = trainOrderAppService.getCallPayInfo(callPayReq.getId());
                    break;
                case BUS_TICKET:
//                callPayDTO = busOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                case SHIP_TICKET:
//                callPayDTO = shipOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                case ATTRACTION_TICKET:
                    callPayDTO = scenicOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                case LOCAL_FUN:
                    callPayDTO = iLocalFunAppOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                case VISA:
//                callPayDTO = getCallPayInfo(callPayReq.getId());
                    break;
                case OUTBOUND_TRAVEL:
                    //出境游
                    callPayDTO = seriesOrderService.getCallPayInfo(callPayReq.getId(), systemBusinessType.getCode());
                    break;
                case INBOUND_TRAVEL:
                    //入境游
                    callPayDTO = seriesOrderService.getCallPayInfo(callPayReq.getId(), systemBusinessType.getCode());
                    break;
                case AIR_SHOW:
                    // 珠海航展
                    callPayDTO = iAirshowAppOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                case FIXED_PACKAGE_TICKET:
                    // 固定套票
                    callPayDTO = iSeasonOrderService.getCallPayInfo(callPayReq.getId());
                    break;
                default:
                    Result.failed(SystemError.SYS_437);
            }
            log.info("【支付服务-查询业务订单数据】查询业务响应, data:{}", JSON.toJSONString(callPayDTO));
            Assert.nonNull(callPayDTO, SystemError.PAY_22003);
            ValidatorUtil.validate(callPayDTO);
            //保存记录
            PayRecord payRecordIns = new PayRecord();
            payRecordIns.setId(IdWorker.getId());
            payRecordIns.setOrderId(req.getOrderId());
            payRecordIns.setOrderNumber(callPayDTO.getOrderNumber());
            payRecordIns.setBusinessTypeCode(systemBusinessType.getCode());
            payRecordIns.setPaymentMethod(req.getPaymentMethod().getCode());
            payRecordIns.setPaymentMethodName(req.getPaymentMethod().getName());
            payRecordIns.setCurrency(callPayDTO.getCurrency().getCode());
            payRecordIns.setTotalFee(callPayDTO.getTotalFee());
            payRecordIns.setSubject(callPayDTO.getSubject());
            payRecordIns.setBody(callPayDTO.getBody());
            payRecordIns.setOrderStatus(PayOrderStatus.UNPAID.getStatus());
            payRecordIns.setPayReqParam(JSON.toJSONString(req));
            payRecordIns.setCreateId(callPayDTO.getSalesmanId());
            payRecordIns.setModifiedId(callPayDTO.getSalesmanId());
            payRecordIns.setNotifyUrl(callPayDTO.getNotifyUrl());
            payRecordIns.setIsDeleted(IsDeletedCode.NO.getCode());
            payRecordIns.setGmtCreate(now);
            payRecordIns.setGmtModified(now);
            int num = recordMapper.insert(payRecordIns);
            Assert.isTrue(num > 0, SystemError.PAY_22002);
            req.setTotalFee(callPayDTO.getTotalFee());
            req.setSubject(callPayDTO.getSubject());
            req.setBody(callPayDTO.getBody());
        } else {
            if (payRecord.getOrderStatus() >= 2) {
                log.info("【支付服务-回调支付】支付记录不是<未支付>状态，无法重复支付, data:{}", payRecord);
                Result.failed(SystemError.PAY_22019);
            }
            int num = recordMapper.update(null, new UpdateWrapper<PayRecord>().lambda()
                    .set(PayRecord::getGmtModified, now)
                    .set(PayRecord::getPaymentMethod, req.getPaymentMethod().getCode())
                    .set(PayRecord::getPaymentMethodName, req.getPaymentMethod().getName())
                    .eq(PayRecord::getOrderId, callPayReq.getId()));
            Assert.isTrue(num > 0, SystemError.PAY_22002);
            req.setSubject(payRecord.getSubject());
            req.setBody(payRecord.getBody());
            req.setTotalFee(payRecord.getTotalFee());
        }

        // 测试环境金额改为0.1
        /*if ("local".equals(active) || "test".equals(active)) {
            req.setTotalFee(new BigDecimal("0.1"));
        }*/
        //发起支付
        log.info("【支付服务-支付】支付方法入参,data:{},active:{}", req, active);
        Object pay = null;
        try {
            pay = PayContext.pay(req, PaymentMethodClass.getClassByCode(callPayReq.getPaymentMethodCode()));
        } catch (Exception e) {
            log.error("【支付服务-支付】支付异常", e);
        }
        Assert.nonNull(pay, SystemError.PAY_22003);
        return Result.success(pay);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object callback(Map<String, String> paramMap) {
        //获取订单号
        String orderNumber = PayUtil.getOrderNumber(paramMap);
        PayRecord payRecord = null;
        if (StringUtils.isNotBlank(orderNumber)) {
            //去数据库获取订单信息
            payRecord = recordMapper.selectByOrderId(Long.valueOf(orderNumber));
        }
        Assert.nonNull(payRecord, SystemError.PAY_22004);
        if (payRecord.getOrderStatus() >= 2) {
            log.info("【支付服务-回调支付】支付记录不是<未支付>状态，无法重复支付, data:{}", payRecord);
            return "SUCCESS";
        }
        log.info("【支付服务-回调支付】支付方法入参, data:{}, paymentType:{}, active:{}", payRecord, payRecord.getPaymentMethodName(), active);
        Object callback = PayContext.callback(paramMap, payRecord.getId(), PaymentMethodClass.getClassByCode(payRecord.getPaymentMethod()));
        return callback;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RefundStatus refund(RefundReq req) {
        //查询支付记录
        PayRecord record = recordMapper.selectByOrderId(Long.valueOf(req.getOrderNumber()));
        Assert.nonNull(record, SystemError.PAY_22004);
        //查询该订单是否已经发起了退款
        Integer count = refundRecordMapper.selectCount(Wrappers.<RefundRecord>lambdaQuery()
                .eq(RefundRecord::getRefundNumber, req.getRefundNumber())
                .ne(RefundRecord::getRefundStatus, RefundStatus.FAILURE)
        );
        Assert.isTrue(count < 1, SystemError.PAY_22011);
        PayRefundReq param = EntityUtil.copy(req, PayRefundReq.class);
        param.setRefundAmount(record.getTotalFee());
        param.setCurrency(record.getCurrency());
        param.setPayCallbackParameter(JSON.parseObject(record.getCallbackParam()));
        log.info("【支付服务-退款】支付方法入参, data:{}, active:{}", param, active);
        RefundStatus refund = PayContext.refund(param, PaymentMethodClass.getClassByCode(record.getPaymentMethod()));
        return refund;
    }

    @Override
    public Result<Integer> pollingPayResult(Long orderId) {
        PayRecord payRecord = recordMapper.selectOne(new QueryWrapper<PayRecord>().lambda()
                .eq(PayRecord::getOrderId, orderId)
                .eq(PayRecord::getIsDeleted, IsDeletedCode.NO.getCode())
                .select(PayRecord::getId, PayRecord::getOrderNumber, PayRecord::getOrderStatus));
        if (payRecord == null) {
            throw new BusinessException(SystemError.SYS_411);
        }
        return Result.success(payRecord.getOrderStatus());
    }
}
